﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using SurveySystem.Repository;
using SurveySystem.Repository.MySQL;
using SurveySystem.Model;
using SurveySystem.GUI.Forms;
using SurveySystem.Factory;
using SurveySystem.Controllers;
using KinectCore.Gui.Controller;
using xn;

namespace SurveySystem.Controllers
{

    public delegate void CLoseEvent();

    /// <summary>
    /// Class to control the selection of surveys
    /// </summary>
    public class SureveySelectionController
    {
        private enum SelectionPanelOptions { REFRESH, HIDE, POPULATE };

        public event CLoseEvent Close;

        // Survey Stuff
        private SurveyFactory surveyFactory;
        private ResultFactory resultFactory;
        private ISurveyRepository surveyRepository;
        private List<Survey> surveyList;

        private SurveySelectionPanel selectionPanel;

        private Control activeControl;

        private System.Timers.Timer buttonTimer;
        private const int timeLimit = 3000;
        private int elapsedTime = 0;
        private int currentPage;
        private int selectedIndex;

        public SurveyMainForm MainForm { get; private set; }

        public SureveySelectionController(SurveyMainForm mainForm, SurveyFactory surveyFactory, ResultFactory resultFactory, ISurveyRepository repository)
        {
            this.surveyRepository = repository;
            surveyList = surveyRepository.GetAllActiveSurveys();

            this.surveyFactory = surveyFactory;
            this.resultFactory = resultFactory;

            currentPage = 1;
            selectedIndex = -1;

            MainForm = mainForm;

            selectionPanel = new SurveySelectionPanel();

            #region " Add Handlers for Navigation Buttons "

            // add handlers for the back button
            selectionPanel.btnBack.MouseEnter += new EventHandler(selectionPanel_anyButton_MouseEnter);
            selectionPanel.btnBack.MouseLeave += new EventHandler(selectionPanel_btnBack_MouseLeave);
            selectionPanel.btnBack.Click += new EventHandler(selectionPanel_btnBack_Click);

            // add handlers for the next button
            selectionPanel.btnNext.MouseEnter += new EventHandler(selectionPanel_anyButton_MouseEnter);
            selectionPanel.btnNext.MouseLeave += new EventHandler(selectionPanel_btnNext_MouseLeave);
            selectionPanel.btnNext.Click += new EventHandler(selectionPanel_btnNext_Click);

            // add handlers for the start button
            selectionPanel.btnStart.MouseEnter += new EventHandler(selectionPanel_anyButton_MouseEnter);
            selectionPanel.btnStart.MouseLeave += new EventHandler(selectionPanel_btnStart_MouseLeave);
            selectionPanel.btnStart.Click += new EventHandler(selectionPanel_btnStart_Click);

            // add handlers for the exit button
            selectionPanel.btnExit.MouseEnter += new EventHandler(selectionPanel_anyButton_MouseEnter);
            selectionPanel.btnExit.MouseLeave += new EventHandler(selectionPanel_btnExit_MouseLeave);
            selectionPanel.btnExit.Click += new EventHandler(selectionPanel_btnExit_Click);

            #endregion

            #region " Add Handlers for Survey Selection Buttons "

            // add handlers for option 1
            selectionPanel.ansOption1.btnSelect.MouseEnter += new EventHandler(selectionPanel_anyButton_MouseEnter);
            selectionPanel.ansOption1.btnSelect.MouseLeave += new EventHandler(selectionPanel_anyButton_MouseLeave);
            selectionPanel.ansOption1.btnSelect.Click += new EventHandler(selectionPanel_anyButton_Click);

            // add handlers for option 2
            selectionPanel.ansOption2.btnSelect.MouseEnter += new EventHandler(selectionPanel_anyButton_MouseEnter);
            selectionPanel.ansOption2.btnSelect.MouseLeave += new EventHandler(selectionPanel_anyButton_MouseLeave);
            selectionPanel.ansOption2.btnSelect.Click += new EventHandler(selectionPanel_anyButton_Click);

            // add handlers for option 3
            selectionPanel.ansOption3.btnSelect.MouseEnter += new EventHandler(selectionPanel_anyButton_MouseEnter);
            selectionPanel.ansOption3.btnSelect.MouseLeave += new EventHandler(selectionPanel_anyButton_MouseLeave);
            selectionPanel.ansOption3.btnSelect.Click += new EventHandler(selectionPanel_anyButton_Click);

            // add handlers for option 4
            selectionPanel.ansOption4.btnSelect.MouseEnter += new EventHandler(selectionPanel_anyButton_MouseEnter);
            selectionPanel.ansOption4.btnSelect.MouseLeave += new EventHandler(selectionPanel_anyButton_MouseLeave);
            selectionPanel.ansOption4.btnSelect.Click += new EventHandler(selectionPanel_anyButton_Click);

            // add handlers for option 5
            selectionPanel.ansOption5.btnSelect.MouseEnter += new EventHandler(selectionPanel_anyButton_MouseEnter);
            selectionPanel.ansOption5.btnSelect.MouseLeave += new EventHandler(selectionPanel_anyButton_MouseLeave);
            selectionPanel.ansOption5.btnSelect.Click += new EventHandler(selectionPanel_anyButton_Click);

            // add handlers for option 6
            selectionPanel.ansOption6.btnSelect.MouseEnter += new EventHandler(selectionPanel_anyButton_MouseEnter);
            selectionPanel.ansOption6.btnSelect.MouseLeave += new EventHandler(selectionPanel_anyButton_MouseLeave);
            selectionPanel.ansOption6.btnSelect.Click += new EventHandler(selectionPanel_anyButton_Click);

            #endregion
        }

        #region " Button Control Timers "

        /// <summary>
        /// Stops the button timer and sets the text on the active control
        /// </summary>
        /// <param name="text">The text to display on the active control</param>
        void stopButtonTimer(string text)
        {
            buttonTimer.Stop();
            elapsedTime = 0;

            // if the active control is not null update the text of the control and set teh active control to null
            if (activeControl != null)
            {
                activeControl.Invoke(new UpdateTextCallback(this.UpdateText), new Object[] { text });
                activeControl = null;
            }
        }

        /// <summary>
        /// Starts the button timer
        /// </summary>
        void startButtonTimer()
        {
            buttonTimer = new System.Timers.Timer(1000);
            // add a handler for when the time elapses
            buttonTimer.Elapsed += new System.Timers.ElapsedEventHandler(buttonTimer_Elapsed);
            elapsedTime = 0;
            buttonTimer.Start();
        }

        /// <summary>
        /// Handles when the button timer elapses
        /// </summary>
        /// <param name="sender">The sender</param>
        /// <param name="e">Event args</param>
        void buttonTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
        {
            // increase the elapsed time
            elapsedTime++;
            // update the text on the active control to the elsapsed time
            activeControl.Invoke(new UpdateTextCallback(this.UpdateText), new Object[] { elapsedTime.ToString() });

            // if the elapsed time reaches the time limit activate the control
            if (elapsedTime * 1000 == timeLimit)
            {
                ControlActivated(activeControl);
            }
        }

        #endregion

        #region " Gui Threading Workarounds "

        /// <summary>
        /// Delegate to handle when the user wishes to exit the application
        /// </summary>
        private delegate void ExitApplicationCallback();

        /// <summary>
        /// Method to exit the application
        /// </summary>
        private void ExitApplication()
        {
            Close();
        }


        /// <summary>
        /// Delegate to update the text on the active control
        /// </summary>
        /// <param name="text">The text to display on the active control</param>
        private delegate void UpdateTextCallback(string text);

        /// <summary>
        /// Updates the text on the active control
        /// </summary>
        /// <param name="text">The text to display on the active control</param>
        private void UpdateText(string text)
        {
            activeControl.Text = text;
        }

        /// <summary>
        /// Delegate to update the selection screen
        /// </summary>
        /// <param name="page">The page to display</param>
        private delegate void UpdateSelectionScreenCallback(SelectionPanelOptions operation, int page);

        /// <summary>
        /// Updates the selection screen
        /// </summary>
        /// <param name="page">The page to display</param>
        private void UpdateSelectionScreen(SelectionPanelOptions operation, int page)
        {
            switch(operation)
            {
                case SelectionPanelOptions.HIDE:
                    hideOptions();
                    break;
                case SelectionPanelOptions.POPULATE:
                    displaySurveyPage(page);
                    break;
                default:
                    break;
            }

            int currentIndex = 6 * (page - 1) + 1;

            selectionPanel.ansOption1.btnSelect.Text = (selectedIndex == currentIndex) ? "Selected" : "Select";
            selectionPanel.ansOption1.btnSelect.BackColor = (selectedIndex == currentIndex++) ? Color.LightGreen : selectionPanel.ansOption1.ButtonBackColor;

            selectionPanel.ansOption2.btnSelect.Text = (selectedIndex == currentIndex) ? "Selected" : "Select";
            selectionPanel.ansOption2.btnSelect.BackColor = (selectedIndex == currentIndex++) ? Color.LightGreen : selectionPanel.ansOption2.ButtonBackColor;

            selectionPanel.ansOption3.btnSelect.Text = (selectedIndex == currentIndex) ? "Selected" : "Select";
            selectionPanel.ansOption3.btnSelect.BackColor = (selectedIndex == currentIndex++) ? Color.LightGreen : selectionPanel.ansOption3.ButtonBackColor;

            selectionPanel.ansOption4.btnSelect.Text = (selectedIndex == currentIndex) ? "Selected" : "Select";
            selectionPanel.ansOption4.btnSelect.BackColor = (selectedIndex == currentIndex++) ? Color.LightGreen : selectionPanel.ansOption4.ButtonBackColor;

            selectionPanel.ansOption5.btnSelect.Text = (selectedIndex == currentIndex) ? "Selected" : "Select";
            selectionPanel.ansOption5.btnSelect.BackColor = (selectedIndex == currentIndex++) ? Color.LightGreen : selectionPanel.ansOption5.ButtonBackColor;

            selectionPanel.ansOption6.btnSelect.Text = (selectedIndex == currentIndex) ? "Selected" : "Select";
            selectionPanel.ansOption6.btnSelect.BackColor = (selectedIndex == currentIndex++) ? Color.LightGreen : selectionPanel.ansOption6.ButtonBackColor;

            selectionPanel.btnStart.Visible = (selectedIndex != -1);
        }

        private void SelectResponse(int response)
        {
            int newIndex = 6 * (currentPage - 1) + response;

            // if new index = current selected index, unselect option
            if (selectedIndex == newIndex)
                selectedIndex = -1;
            else
                selectedIndex = newIndex;

            selectionPanel.Invoke(new UpdateSelectionScreenCallback(this.UpdateSelectionScreen), new Object[] { SelectionPanelOptions.REFRESH, currentPage });
        }

        #endregion

        #region " Selection Screen Display Handling Code "

        /// <summary>
        /// Hides all of the options
        /// </summary>
        private void hideOptions()
        {
            selectionPanel.ansOption1.Visible = false;
            selectionPanel.ansOption2.Visible = false;
            selectionPanel.ansOption3.Visible = false;
            selectionPanel.ansOption4.Visible = false;
            selectionPanel.ansOption5.Visible = false;
            selectionPanel.ansOption6.Visible = false;
        }

        /// <summary>
        /// Displays a page of surveys
        /// </summary>
        /// <param name="page">The page to display</param>
        private void displaySurveyPage(int page)
        {

            string labelText = "Showing Surveys {0} to {1} of {2}";
            int startIndex = 6 * (page - 1) + 1;

            // check to see if we are trying to view a page that should not exist
            if (surveyList.Count > 6 * page)
            {
                // TODO: error handling code here
            }

            // calcualte the end index
            int endIndex;
            if (startIndex + 5 < surveyList.Count)
            {
                endIndex = startIndex + 5;
            }
            else
            {
                endIndex = surveyList.Count;
            }

            UpdateNavigationButtons(startIndex, endIndex);

            // stops the panel divider showing on sceen
            selectionPanel.btnExit.Focus();

            // update the label displaying inforamtion about the surveys in view
            selectionPanel.lblSurveyList.Text = String.Format(labelText, startIndex, endIndex, surveyList.Count);

            PopulateOptions(startIndex);

        }

        /// <summary>
        /// Populates the list of visible options
        /// </summary>
        /// <param name="currentIndex">The index of the first option to display</param>
        private void PopulateOptions(int currentIndex)
        {
            if (currentIndex <= surveyList.Count)
            {
                selectionPanel.ansOption1.Text = surveyList[currentIndex - 1].Title;
                selectionPanel.ansOption1.Visible = true;
            }

            currentIndex++;

            if (currentIndex <= surveyList.Count)
            {
                selectionPanel.ansOption2.Text = surveyList[currentIndex - 1].Title;
                selectionPanel.ansOption2.Visible = true;
            }

            currentIndex++;

            if (currentIndex <= surveyList.Count)
            {
                selectionPanel.ansOption3.Text = surveyList[currentIndex - 1].Title;
                selectionPanel.ansOption3.Visible = true;
            }

            currentIndex++;

            if (currentIndex <= surveyList.Count)
            {
                selectionPanel.ansOption4.Text = surveyList[currentIndex - 1].Title;
                selectionPanel.ansOption4.Visible = true;
            }

            currentIndex++;

            if (currentIndex <= surveyList.Count)
            {
                selectionPanel.ansOption5.Text = surveyList[currentIndex - 1].Title;
                selectionPanel.ansOption5.Visible = true;
            }

            currentIndex++;

            if (currentIndex <= surveyList.Count)
            {
                selectionPanel.ansOption6.Text = surveyList[currentIndex - 1].Title;
                selectionPanel.ansOption6.Visible = true;
            }

        }

        /// <summary>
        /// Decides if the navigation buttons (next/back) should be visible or not
        /// </summary>
        /// <param name="startIndex">The index of the first option displayed on the screen</param>
        /// <param name="endIndex">The index of the last option displayed on the screen</param>
        private void UpdateNavigationButtons(int startIndex, int endIndex)
        {
            
            // if the first option displayed on the screen IS NOT the first option (we are not on page 1) display the back button
            if (startIndex > 1)
            {
                selectionPanel.btnBack.Visible = true;
            }
            // if the first option displayed on the screen IS the first option (we are on page 1) do not display the back button
            else
            {
                selectionPanel.btnBack.Visible = false;
            }

            // if the last option displayed on the screen IS NOT the last option (we are not viewing the last page of options) display the next button
            if (endIndex < surveyList.Count)
            {
                selectionPanel.btnNext.Visible = true;
            }
            // if the last option displayed on the screen IS the last option (we are viewing the last page of options) do not display the next button
            else
            {
                selectionPanel.btnNext.Visible = false;
            }
        }

        #endregion

        #region " Handlers for Buttons "

        #region " General "

        /// <summary>
        /// Handles when a control is activated
        /// </summary>
        /// <param name="c">The control that is activated</param>
        private void ControlActivated(Control c)
        {
            if (c == null)
                return;

            if (c == selectionPanel.btnStart)
            {
                selectionPanel_btnStart_Click(null, null);
            }
            else if (c == selectionPanel.btnExit)
            {
                selectionPanel_btnExit_Click(null, null);
            }
            else if (c == selectionPanel.btnNext)
            {
                selectionPanel_btnNext_Click(null, null);
            }
            else if (c == selectionPanel.btnBack)
            {
                selectionPanel_btnBack_Click(null, null);
            }
            else if (c == selectionPanel.ansOption1.btnSelect || c == selectionPanel.ansOption2.btnSelect
                || c == selectionPanel.ansOption3.btnSelect || c == selectionPanel.ansOption4.btnSelect
                || c == selectionPanel.ansOption5.btnSelect || c == selectionPanel.ansOption6.btnSelect)
            {
                selectionPanel_anyButton_Click(c, null);
            }
        }

        #endregion

        #region " Handlers for Navigation Buttons "

        /// <summary>
        /// Handles when the section panel's back button is clicked
        /// </summary>
        /// <param name="sender">The Sender</param>
        /// <param name="e">Event args</param>
        void selectionPanel_btnBack_Click(object sender, EventArgs e)
        {
            selectionPanel_btnBack_MouseLeave(null, null);

            // hides all options
            selectionPanel.Invoke(new UpdateSelectionScreenCallback(this.UpdateSelectionScreen), new Object[] { SelectionPanelOptions.HIDE, null });
            // populates new options
            selectionPanel.Invoke(new UpdateSelectionScreenCallback(this.UpdateSelectionScreen), new Object[] { SelectionPanelOptions.POPULATE, --currentPage });
        }

        /// <summary>
        /// Handles when the mouse leaves the section panel's back button
        /// </summary>
        /// <param name="sender">The Sender</param>
        /// <param name="e">Event args</param>
        void selectionPanel_btnBack_MouseLeave(object sender, EventArgs e)
        {
            stopButtonTimer("<< Back");
        }

        /// <summary>
        /// Handles when the section panel's next button is clicked
        /// </summary>
        /// <param name="sender">The Sender</param>
        /// <param name="e">Event args</param>
        void selectionPanel_btnNext_Click(object sender, EventArgs e)
        {
            selectionPanel_btnNext_MouseLeave(null, null);

            // hides all options
            selectionPanel.Invoke(new UpdateSelectionScreenCallback(this.UpdateSelectionScreen), new Object[] { SelectionPanelOptions.HIDE, null });
            // populates new options
            selectionPanel.Invoke(new UpdateSelectionScreenCallback(this.UpdateSelectionScreen), new Object[] { SelectionPanelOptions.POPULATE, ++currentPage });
        }

        /// <summary>
        /// Handles when the mouse leaves the section panel's next button
        /// </summary>
        /// <param name="sender">The Sender</param>
        /// <param name="e">Event args</param>
        void selectionPanel_btnNext_MouseLeave(object sender, EventArgs e)
        {
            stopButtonTimer("Next >>");
        }

        /// <summary>
        /// Handles when the section panel's start button is clicked
        /// </summary>
        /// <param name="sender">The Sender</param>
        /// <param name="e">Event args</param>
        void selectionPanel_btnStart_Click(object sender, EventArgs e)
        {

            Survey s = surveyRepository.GetAllActiveSurveys()[selectedIndex - 1];
            surveyFactory.PopulateSurvey(s);

            SurveyController cont = new SurveyController(MainForm, null, s, resultFactory);
            cont.SurveyFinished += new SurveyFinishedEvent(cont_SurveyFinished);
            cont.ShowSurveyWelcome();

        }

        void cont_SurveyFinished()
        {
            // TODO: desturct survey controller or something???
            selectedIndex = -1;
            ShowSelectionScreen();
        }

        /// <summary>
        /// Handles when the mouse leaves the section panel's start button
        /// </summary>
        /// <param name="sender">The Sender</param>
        /// <param name="e">Event args</param>
        void selectionPanel_btnStart_MouseLeave(object sender, EventArgs e)
        {
            stopButtonTimer("Start Survey");
        }

        /// <summary>
        /// Handles when the section panel's exit button is clicked
        /// </summary>
        /// <param name="sender">The Sender</param>
        /// <param name="e">Event args</param>
        void selectionPanel_btnExit_Click(object sender, EventArgs e)
        {
            selectionPanel.Invoke(new ExitApplicationCallback(this.ExitApplication));
        }

        /// <summary>
        /// Handles when the mouse leaves the section panel's eixt button
        /// </summary>
        /// <param name="sender">The Sender</param>
        /// <param name="e">Event args</param>
        void selectionPanel_btnExit_MouseLeave(object sender, EventArgs e)
        {
            stopButtonTimer("Exit");
        }

        #endregion

        #region " Handlers for Survey Selection Buttons "

        /// <summary>
        /// Handles the button click of the buttons
        /// </summary>
        /// <param name="sender">The Button</param>
        /// <param name="e">Event args</param>
        void selectionPanel_anyButton_Click(object sender, EventArgs e)
        {
            stopButtonTimer("");

            if (sender == selectionPanel.ansOption1.btnSelect)
            {
                SelectResponse(1);
            }
            else if (sender == selectionPanel.ansOption2.btnSelect)
            {
                SelectResponse(2);
            }
            else if (sender == selectionPanel.ansOption3.btnSelect)
            {
                SelectResponse(3);
            }
            else if (sender == selectionPanel.ansOption4.btnSelect)
            {
                SelectResponse(4);
            }
            else if (sender == selectionPanel.ansOption5.btnSelect)
            {
                SelectResponse(5);
            }
            else if (sender == selectionPanel.ansOption6.btnSelect)
            {
                SelectResponse(6);
            }

        }

        /// <summary>
        /// Handles when the mouse leaves the button
        /// </summary>
        /// <param name="sender">The Button</param>
        /// <param name="e">Event args</param>
        void selectionPanel_anyButton_MouseLeave(object sender, EventArgs e)
        {
            // stop the timer and set the text to "Select"
            stopButtonTimer("Select");
        }

        /// <summary>w
        /// Handlers when the mouse enteres a button
        /// </summary>
        /// <param name="sender">The Button</param>
        /// <param name="e">Event args</param>
        void selectionPanel_anyButton_MouseEnter(object sender, EventArgs e)
        {
            // set the next button to be the active control
            activeControl = (Control)sender;
            startButtonTimer();
        }

        #endregion

        #endregion

        #region " Survey Selection Navigation Methods "

        /// <summary>
        /// Shows the selection screen
        /// </summary>
        public void ShowSelectionScreen()
        {
            if (surveyRepository == null)
            {
                // do nothing
            }
            else
            {
                lock (surveyRepository)
                {
                    surveyList = surveyRepository.GetAllActiveSurveys();

                    // remove any other controls from the main form
                    MainForm.Controls.Clear();
                    // add the selection panel and make visible
                    selectionPanel.Dock = System.Windows.Forms.DockStyle.Fill;
                    MainForm.Controls.Add(selectionPanel);
                    MainForm.ThemeBackColor = Color.White;
                    MainForm.ThemeForeColor = Color.Navy;
                    MainForm.ConvertToTheme(selectionPanel.Controls);
                    MainForm.ConvertToTheme(selectionPanel.ansOption1);
                    MainForm.ConvertToTheme(selectionPanel.ansOption2);
                    MainForm.ConvertToTheme(selectionPanel.ansOption3);
                    MainForm.ConvertToTheme(selectionPanel.ansOption4);
                    MainForm.ConvertToTheme(selectionPanel.ansOption5);
                    MainForm.ConvertToTheme(selectionPanel.ansOption6);
                    MainForm.Visible = true;

                    // update the display
                    selectionPanel.Invoke(new UpdateSelectionScreenCallback(this.UpdateSelectionScreen), new Object[] { SelectionPanelOptions.HIDE, null });
                    selectionPanel.Invoke(new UpdateSelectionScreenCallback(this.UpdateSelectionScreen), new Object[] { SelectionPanelOptions.POPULATE, currentPage });

                }
            }
        }

        #endregion

    }
}
